//
// Created by cjh on 2021/5/10.
//

#ifndef RESOURCE_LOOP_H
#define RESOURCE_LOOP_H
#include <map>
#include <memory>
#include <functional>
#include <coroutine>
#include <chrono>
#include "epoll.h"


class asocket;


class event_loop
{
public:
    //! user interface
    explicit event_loop() : sleep(this) {}
    void run(bool forever=false);
    void quit();
    void force_quit();

    //!  socket event
    void add_socket(asocket* ptr);
    void rmv_socket(int fd);
    void mod_socket(int fd, int event_mask);

    //!  timeout event
    typedef std::function<void()> timeout_callback;
    void set_timeout(int msec, timeout_callback&&);
    void set_timeout(std::chrono::system_clock::time_point && t, timeout_callback&& callback);
    //! asleep
    class _asleep {
    public:
        struct _awaiter {
            explicit _awaiter(event_loop *loop, int msec)
                :timepoint(std::chrono::system_clock::now()+std::chrono::milliseconds(msec)),
                 loop(loop) {}
            inline bool await_ready()const noexcept { return false; }
            void await_suspend(std::coroutine_handle<>&&handle) {
                loop->set_timeout(std::move(timepoint), [handle]() {
                    handle.resume();
                });
            }
            inline void await_resume() {}
            std::chrono::system_clock::time_point timepoint;
            event_loop *loop;
        };

        explicit _asleep(event_loop *loop) :loop(loop) {}
        _awaiter operator()(int msec) { return _awaiter(loop, msec); }

    private:
        event_loop *loop;
    } sleep;

private:
    //  config
    int running = false;
    int minimal_cycle_period = 10; // msec

    //  epoll
    epoll ep;
    std::map<int, asocket*> epoll_events;

    //  timeout, structure using minimal heap
    struct timeout_element {
        explicit timeout_element(int dur=0, timeout_callback&& cbk=nullptr)
            :timeout_element(std::chrono::system_clock::now() + std::chrono::milliseconds(dur),
                             std::forward<timeout_callback>(cbk)) {}
        explicit timeout_element(std::chrono::system_clock::time_point &&t, timeout_callback&& cbk= nullptr)
            :timepoint(std::forward<std::chrono::system_clock::time_point>(t)),
             callback(std::forward<timeout_callback>(cbk)) {}
        std::chrono::system_clock::time_point timepoint;
        timeout_callback callback;

        inline bool operator<(const timeout_element& ele) { return timepoint < ele.timepoint; }
        inline bool operator>(const timeout_element& ele) { return !(*this < ele); }
    };
    std::vector<timeout_element> time_events;
};


#endif //RESOURCE_LOOP_H
